﻿<%@ Page Language="C#" ContentType="text/cache-manifest" ResponseEncoding = "utf-8" %>

<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.Xml.Serialization" %>
<script runat="server">
    private string configFile;
    protected void Page_Load(object sender, EventArgs e)
    {
        configFile = Server.MapPath("~/ManifestController.config");
        if (!File.Exists(configFile))
        {
            Response.Write("配置文件不存在");
            Response.End();
            return;
        }

        //判断是否有更新
        XElement element = XElement.Load(configFile);
        var config = XmlSerializerHelper.DeserializeToEntity<ManifestControllerModel>(element.ToString());
        switch (config.UpdateMode)
        {
            case 0://依赖文件更新
                if (config.Updated)
                {
                    config.Updated = false;//标记为未更新
                    SaveToConfigFile(config);
                    Cache["HTML5.ManifestController"] = config;
                    if (Cache["HTML5.ManifestController"] != null)
                    {
                        Cache.Remove("HTML5.ManifestController");//有更新，删除缓存，等待重组
                    }
                }
                break;
            case 1://始终更新
                string manifest = GetManifestFromCache(config);
                Cache["HTML5.ManifestController"] = manifest + System.Environment.NewLine + "#Last Update: "+DateTime.Now.ToString();//强制更新
                break;
            case 2://始终不更新
                break;

            default:
                break;
        }

        string text = GetManifestFromCache(config);//始终从“最新”的缓存中获取
        Response.Write(text);
        Response.End();
    }

    public string GetManifestFromCache(ManifestControllerModel config)
    {
        if (Cache["HTML5.ManifestController"] == null)
        {
            StringBuilder manifest = new StringBuilder();

            //加入缓存
            manifest.AppendLine("CACHE MANIFEST");
            manifest.AppendLine("#Create Time: " + DateTime.Now.ToString());
            manifest.AppendLine("");

            manifest.AppendLine("CACHE:");
            manifest.AppendLine(GetFileList(config.Cache));

            manifest.AppendLine("FALLBACK:");
            manifest.AppendLine(GetFileList(config.Fallback));

            manifest.AppendLine("NETWORK:");
            manifest.AppendLine(GetFileList(config.Network));

            manifest.AppendLine("");
            manifest.AppendLine("#Last Update: " + DateTime.Now.ToString());


            Cache["HTML5.ManifestController"] = manifest.ToString();
        }

        return Cache["HTML5.ManifestController"] as string;
    }

    public string GetFileList(string data)
    {
        StringBuilder files = new StringBuilder();
        foreach (var line in data.Split(new[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries))
        {
            if (line.EndsWith("/"))
            {
                //搜索目录
                var appPath = Path.Combine("~/", line);
                var dir = Server.MapPath(appPath);
                if (Directory.Exists(dir))
                {
                    var directories = new List<string>();
                    GetUsableDirectories(dir, directories);//获取所有可用目录
                    
                    foreach (var usableDir in directories)
                    {
                        //Response.WriteFile(Path.GetFileName(usableDir));

                        var childrenFiles = Directory.GetFiles(usableDir, "*", SearchOption.TopDirectoryOnly);
                        int shortFilePathIndex = Server.MapPath("~/").Length - 1;//路径前缀，用于计算短路径起始字符位置
                        foreach (var childFile in childrenFiles)
                        {
                            int shortPathStart = childFile.IndexOf(line.Replace("/", "\\"), shortFilePathIndex);//实际开始位置
                            string shortFilePath = childFile.Substring(shortPathStart, childFile.Length - shortPathStart);
                            files.AppendLine(shortFilePath.Replace("\\", "/"));
                        }
                    }
                }
                else
                {
                    files.AppendLine(line);//目录不存在，可能是URL重写
                }
            }
            else
            {
                //添加单个文件
                files.AppendLine(line);
            }
        }
        return files.ToString();
    }

    /// <summary>
    /// 递归获取可用文件夹
    /// </summary>
    public void GetUsableDirectories(string dir,List<string> dirList ) {
        if (string.IsNullOrEmpty(dir)|| dirList== null)
        {
            return;
        }
        
        var childrenDir = Directory.GetDirectories(dir, "*", SearchOption.TopDirectoryOnly)
                                    .Where(z => !new[] { ".SVN", "_SVN" }.Contains(Path.GetFileName(z).ToUpper())//筛选不需要列入的目录
                                            && !new[]{".CONFIG" ,".CS"}.Contains(Path.GetExtension(z).ToUpper())//过滤扩展名
                                            && new DirectoryInfo(z).Attributes != FileAttributes.Hidden//不为隐藏文件
                                            ).ToList();//获取符合条件的下级目录
        dirList.Add(dir);
        foreach (var childDir in childrenDir)
        {
            //Response.Write(childDir+"\n\r");
            dirList.Add(childDir);
            GetUsableDirectories(childDir, dirList);
        }
        //Response.Write("\n\r");
        
    }

    /// <summary>
    /// 保存到配置文件
    /// </summary>
    /// <param name="model"></param>
    private void SaveToConfigFile(ManifestControllerModel model)
    {
        string xml = XmlSerializerHelper.SerializerToXml(model);
        XDocument doc = XDocument.Parse(xml);
        doc.Save(configFile);
    }


    [Serializable]
    public class ManifestControllerModel
    {
        public string Cache { get; set; }
        public string Fallback { get; set; }
        public string Network { get; set; }
        public string CacheDependence { get; set; }
        public int UpdateMode { get; set; }
        public bool SyncCacheToCacheDependence { get; set; }
        public bool Updated { get; set; }
    }

    /// <summary>
    /// XML序列化/反序列化帮助类
    /// </summary>
    public class XmlSerializerHelper
    {
        /// <summary>
        /// 实例序列化XML
        /// </summary>
        public static string SerializerToXml<T>(T obj)
        {
            if (obj == null) return null;
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            MemoryStream stream = new MemoryStream();
            XmlTextWriter xtw = new XmlTextWriter(stream, Encoding.UTF8);
            xtw.Formatting = Formatting.Indented;
            try
            {
                serializer.Serialize(stream, obj);
            }
            catch { return null; }

            stream.Position = 0;
            string returnStr = string.Empty;
            using (StreamReader sr = new StreamReader(stream, Encoding.UTF8))
            {
                returnStr = sr.ReadToEnd();
            }
            return returnStr;
        }

        /// <summary>
        /// XML反序列化到实例
        /// </summary>
        public static T DeserializeToEntity<T>(string data)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                using (StreamWriter sw = new StreamWriter(stream, Encoding.UTF8))
                {
                    sw.Write(data);
                    sw.Flush();
                    stream.Seek(0, SeekOrigin.Begin);
                    XmlSerializer serializer = new XmlSerializer(typeof(T));
                    try
                    {
                        return ((T)serializer.Deserialize(stream));
                    }
                    catch
                    {
                        return default(T);
                    }
                }
            }
        }
    }
</script>
